/*
 token.go
 Created by hupfei on 2019-10-17
*/

package model

import (
	"database/sql"
	"fmt"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
	"math"
	"strconv"
	"time"
	"zimuzu_cvn_web_api/config"
	"zimuzu_cvn_web_api/pkg/drive/db"
	. "zimuzu_cvn_web_api/pkg/e"
	"zimuzu_cvn_web_api/pkg/errno"
)

//出售订单的类型常量
const TokenSaleOrderPlatBuy = 1     //官方购买令牌订单
const TokenSaleOrderOutSell = 2     //第三方出售订单
const TokenSaleOrderOutBuy = 3      //第三方购买订单
const TokenSaleOrderPlatBuyback = 4 //官方回购令牌订单
//租借订单的类型常量
const TokenRentOrderRent = 1 //求租订单
const TokenRentOrderlend = 2 //出租订单
//出售订单的支付状态常量
const TokenOrderPayed = 2 //令牌订单已支付常量
const TokenOrderUnPay = 1 //令牌订单未支付常量
//令牌的使用状态常量
const TokenUsedStatusUnused = 1         //令牌闲置中
const TokenUsedStatusMining = 2         //令牌质押中
const TokenUsedStatusLocking = 3        //令牌出售锁定中
const TokenUsedStatusRecycle = 4        //已回收
const TokenUsedStatusBuybackLocking = 5 //令牌回购锁定中
//求租订单的状态常量
const TokenRentOrderStatusRenting = 1  //挂单出租中
const TokenRentOrderStatusComplete = 2 //已完成
const TokenRentOrderStatusCancel = 3   //已撤销
const TokenRentOrderStatusEnd = 4      //已到期
const TokenRentOrderStatusStop = 5     //提前终止
//出售订单的状态常量
const TokenSaleOrderStatusSale = 1     //USDT令牌订单出售中
const TokenSaleOrderStatusLock = 2     //出售订单锁定中
const TokenSaleOrderStatusOverdue = 3  //出售订单已过期
const TokenSaleOrderStatusComplete = 4 //出售订单已完成
const TokenSaleOrderStatusCancel = 5   //出售订单已撤销
const TokenSaleOrderStatusRank = 6     //出售订单排队中
//令牌日志的类型常量
const TokenLogTypeSale = 2               //出售转让
const TokenLogTypeBuyback = 6            //官方回购
const TokenLogTypePledge = 8             //质押令牌
const TokenLogTypeCancelPledge = 9       //撤销令牌
const TokenLogTypeBatchCancelPledge = 10 //批量撤销令牌
//租赁订单的类型常量
const TokenSellOrderRentSeeking = 1 //求租
const TokenSellOrderRentOut = 2     //出租
//租赁订单的状态常量
const TokenSellOrderStatusRenting = 1 //挂单出租中
const TokenSellOrderStatusDeal = 2    //已成交
const TokenSellOrderStatusCancel = 3  //已撤销
const TokenSellOrderStatusExpire = 4  //已到期
const TokenSellOrderStatusEnd = 5     //已终止
//令牌回购排队状态常量
const TokenQueueStatusRankLocking = 1
const TokenQueueStatusExpire = 2
const TokenQueueStatusRanking = 3
const TokenQueueStatusCancel = 4
const TokenQueueStatusComplete = 5

//令牌租用状态
const TokenRentStatusNormal = 1 //未租用
const TokenRentStatusRented = 2 //已租用

//节点质押状态
const NodePledgeStatusNormal = 1 //未质押
const NodePledgeStatusOwn = 2    //使用自有令牌质押
const NodePledgeStatusRent = 3   //使用租用的令牌质押

//一个令牌最多可质押的路由器台数
const OneTokenCanBePledgedMaxNum = 5

type TokenSaleOrder struct {
	Id                    int64   `db:"id" json:"id"`
	OrderNo               string  `db:"order_no" json:"order_no"`
	ParentOrderNo         string  `db:"parent_order_no" json:"parent_order_no"`
	UnitCvntPrice         int     `db:"unit_cvnt_price" json:"unit_cvnt_price"`
	TotalCvntPirce        int     `db:"total_cvnt_pirce" json:"total_cvnt_pirce"`
	UnitUsdtPrice         float64 `db:"unit_usdt_price" json:"unit_usdt_price"`
	TotalUsdtPrice        float64 `db:"total_usdt_price" json:"total_usdt_price"`
	TokenNum              int     `db:"token_num" json:"token_num"`
	OldTokenNum           int     `db:"old_token_num" json:"old_token_num"`
	OldTokenUnitCvntPrice int     `db:"old_token_unit_cvnt_price" json:"old_token_unit_cvnt_price"`
	LockNum               int     `db:"lock_num" json:"lock_num"`
	DealNum               int     `db:"deal_num" json:"deal_num"`
	BuyerUid              int64   `db:"buyer_uid" json:"buyer_uid"`
	SellerUid             int64   `db:"seller_uid" json:"seller_uid"`
	Status                int     `db:"status" json:"status"`
	PayValidTime          int64   `db:"pay_valid_time" json:"pay_valid_time"`
	PayTime               int64   `db:"pay_time" json:"pay_time"`
	PayStatus             int     `db:"pay_status" json:"pay_status"`
	CreateTime            int64   `db:"create_time" json:"create_time"`
	OrderType             int     `db:"type" json:"type"`
	CompleteTime          int64   `db:"complete_time" json:"complete_time"`
}

type TokenRentOrder struct {
	Id            int     `db:"id" json:"id"`
	OrderNo       string  `db:"order_no" json:"order_no"`
	ParentOrderNo string  `db:"parent_order_no" json:"parent_order_no"`
	TokenNum      int     `db:"token_num" json:"token_num"`
	LockNum       int     `db:"lock_num" json:"lock_num"`
	DealNum       int     `db:"deal_num" json:"deal_num"`
	BuyerUid      int64   `db:"buyer_uid" json:"buyer_uid"`
	RentUid       int64   `db:"rent_uid" json:"rent_uid"`
	LendUid       int64   `db:"lend_uid" json:"lend_uid"`
	Scale         float64 `db:"scale" json:"scale"`
	Days          int     `db:"days" json:"days"`
	EndTime       int64   `db:"end_time" json:"end_time"`
	Status        int     `db:"status" json:"status"`
	CreateTime    int64   `db:"create_time" json:"create_time"`
	OrderType     int     `db:"type" json:"type"`
	Info          string  `db:"info" json:"info"`
	CompleteTime  int64   `db:"complete_time" json:"complete_time"`
	UpdateTime    int64   `db:"update_time" json:"update_time"`
}

type TokenDrawQueue struct {
	Id         int64  `db:"id" json:"id"`
	TokenNum   int    `db:"token_num" json:"token_num"`
	OrderNo    string `db:"order_no" json:"order_no"`
	Uid        int64  `db:"uid" json:"uid"`
	Cvn        int64  `db:"cvn" json:"cvn"`
	Status     int    `db:"status" json:"status"`
	CreateTime int64  `db:"create_time" json:"create_time"`
	UpdateTime int64  `db:"update_time" json:"update_time"`
}

type TokenSellOrder struct {
	Id            int64   `db:"id" json:"id"`
	OrderNo       string  `db:"order_no" json:"order_no"`
	ParentOrderNo string  `db:"parent_order_no" json:"parent_order_no"`
	TokenNum      int64   `db:"token_num" json:"token_num"`
	DealNum       int64   `db:"deal_num" json:"deal_num"`
	RentUid       int64   `db:"rent_uid" json:"rent_uid"`
	LendUid       int64   `db:"lend_uid" json:"lend_uid"`
	Scale         float64 `db:"scale" json:"scale"`
	Days          int64   `db:"days" json:"days"`
	EndTime       int64   `db:"end_time" json:"end_time"`
	Status        int64   `db:"status" json:"status"`
	Type          int64   `db:"type" json:"type"`
	Info          string  `db:"info" json:"info"`
	CreateTime    int64   `db:"create_time" json:"create_time"`
	CompleteTime  int64   `db:"complete_time" json:"complete_time"`
	UpdateTime    string  `db:"update_time" json:"update_time"`
}

type CheckRentTokenData struct {
	Status     int     `json:"status"`
	Rent       int     `json:"rent"`
	UnRent     int     `json:"unRent"`
	TotalCvn   float64 `json:"totalCvn"`
	NeedCvn    float64 `json:"needCvn"`
	Cvn        float64 `json:"cvn"`
	EnoughDays int     `json:"enoughDays"`
}

type RentTransData struct {
	OrderNo string `form:"orderNo" json:"orderNo"`
	Num     int64  `form:"num" json:"num"`
	Type    int64  `form:"type" json:"type"`
}

type MgTokenLog struct {
	Id         int64  `db:"id" json:"id"`
	TokenId    int64  `db:"token_id" json:"token_id"`
	SourceUid  int64  `db:"source_uid" json:"source_uid"`
	TargetUid  int64  `db:"target_uid" json:"target_uid"`
	OrderNo    string `db:"order_no" json:"order_no"`
	Type       int64  `db:"type" json:"type"`
	CreateTime int64  `db:"create_time" json:"create_time"`
	NodeId     int64  `db:"node_id" json:"node_id"`
}

//获取令牌数量
func GetTokenNum(belongUid int64, useUid int64, status int, rentStatus int) (num int, err error) {
	sqlStr := "SELECT COUNT(*) FROM mg_token WHERE"
	if status > 0 {
		if status == TokenUsedStatusLocking || status == TokenUsedStatusBuybackLocking {
			sqlStr += fmt.Sprintf(" (status = %d OR status = %d) AND", TokenUsedStatusLocking, TokenUsedStatusBuybackLocking)
		} else {
			sqlStr += fmt.Sprintf(" status = %d AND", status)
		}
	}
	if belongUid > 0 {
		sqlStr += fmt.Sprintf(" belong_uid = %d AND", belongUid)
	}
	if useUid > 0 {
		sqlStr += fmt.Sprintf(" use_uid = %d AND", useUid)
	}
	if rentStatus > 0 {
		sqlStr += fmt.Sprintf(" rent_status = %d AND", rentStatus)
	}
	sqlStr += " status != 4"

	err = sqlx.Get(config.C.DB.DB, &num, sqlStr)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetTokenNum：", err.Error())
	}
	return
}

//获取用户可质押的路由器节点数量（不包含完整令牌）
func GetRouterNodeNumExceptWholeToken(rentStatus int, uid int64) (num int, err error) {
	if rentStatus == TokenRentStatusRented {
		err = sqlx.Get(config.C.DB.DB, &num, "SELECT IFNULL(SUM(?-node_num), 0) FROM mg_token WHERE use_uid = ? AND status = ? AND rent_status = ? AND type = ?",
			OneTokenCanBePledgedMaxNum, uid, TokenUsedStatusMining, TokenRentStatusRented, NodeTypeRouter)
	} else {
		err = sqlx.Get(config.C.DB.DB, &num, "SELECT IFNULL(SUM(?-node_num), 0) FROM mg_token WHERE belong_uid = ? AND status = ? AND rent_status = ? AND type = ?",
			OneTokenCanBePledgedMaxNum, uid, TokenUsedStatusMining, TokenRentStatusNormal, NodeTypeRouter)
	}
	if err != nil {
		Log("err", "失败 GetRouterNodeNumExceptWholeToken：", err.Error())
	}
	return
}

//路由器质押的令牌数量
func GetRouterTokenPledgedNum(uid int64) (num int, err error) {
	err = sqlx.Get(config.C.DB.DB, &num, "SELECT COUNT(*) FROM mg_token WHERE use_uid=? AND status=? AND type = ?",
		uid, TokenUsedStatusMining, NodeTypeRouter)
	if err != nil {
		Log("err", "失败 GetRouterTokenPledgedNum：", err.Error())
	}
	return
}

type ReturnTokenInfo struct {
	DealNum       int `db:"deal_num" json:"dealNum"`
	RemainingTime int `db:"remainingTime" json:"remainingTime"`
}

//近日需归还的令牌信息
func GetRecentlyReturnToken(uid int64) (info ReturnTokenInfo, err error) {
	err = sqlx.Get(config.C.DB.DB, &info, "SELECT SUM(deal_num) AS deal_num, IF(end_time < UNIX_TIMESTAMP(date_add(curdate(),INTERVAL 1 DAY)), 0, "+
		"CEIL((end_time-UNIX_TIMESTAMP(NOW())) / (60*60*24))) AS remainingTime "+
		"FROM mg_token_sell_order WHERE type = 2 AND lend_uid = ? AND status = 2 AND end_time > UNIX_TIMESTAMP(NOW()) GROUP BY FROM_UNIXTIME(end_time, '%Y-%m-%d') ORDER BY end_time ASC, id DESC LIMIT 1", uid)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetRecentlyReturnToken：", err.Error())
	}
	return
}

//今日linux和pc总质押数
func GetTodayPledgeNum() (num int, err error) {
	err = sqlx.Get(config.C.DB.DB, &num, "SELECT COUNT(*) FROM mg_node WHERE (type = 1 OR type = 2) AND token_status != 1 AND token_id > 0 AND pledge_time >= UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE))")
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetTodayPledgeNum：", err.Error())
	}
	return
}

//借入总数
func GetBorrowNum(uid int64) (count int, err error) {
	err = sqlx.Get(config.C.DB.DB, &count, "SELECT COUNT(*) FROM mg_token_sell_order WHERE lend_uid = ? AND status = 2 AND type = 2", uid)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetBorrowNum：", err.Error())
	}
	return
}

// 生成官方购买令牌订单
//Auth: Xu
func InsertTokenSaleOrder(db sqlx.Execer, data TokenSaleOrder) (err error) {
	payStatus := TokenOrderUnPay
	if data.PayStatus == TokenOrderPayed {
		payStatus = TokenOrderPayed
	}
	_, err = db.Exec("INSERT INTO mg_token_sale_order (order_no, parent_order_no, `unit_cvnt_price`, total_cvnt_pirce, unit_usdt_price, total_usdt_price, token_num,old_token_num,old_token_unit_cvnt_price, buyer_uid, seller_uid, pay_valid_time, `type`,status, create_time, pay_status, deal_num, complete_time) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", data.OrderNo, data.ParentOrderNo, data.UnitCvntPrice, data.TotalCvntPirce, data.UnitUsdtPrice, data.TotalUsdtPrice, data.TokenNum, data.OldTokenNum, data.OldTokenUnitCvntPrice, data.BuyerUid, data.SellerUid, data.PayValidTime, data.OrderType, data.Status, data.CreateTime, payStatus, data.DealNum, data.CompleteTime)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

// 生成官方回购队列记录
//Auth: Xu
func InsertBuybackQueue(db sqlx.Execer, data TokenDrawQueue) (err error) {
	_, err = db.Exec("INSERT INTO mg_token_draw_queue (uid,token_num,order_no,cvn,status, create_time) values(?,?,?,?,?,?)", data.Uid, data.TokenNum, data.OrderNo, data.Cvn, data.Status, data.CreateTime)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//根据订单号查询订单信息
//Auth:Xu
func GetTokenOrderInfoByNo(db sqlx.Queryer, orderNo string) (orderInfo TokenSaleOrder, err error) {
	err = sqlx.Get(db, &orderInfo, "SELECT id, order_no,status,type,lock_num ,seller_uid,deal_num, parent_order_no,pay_status,buyer_uid, create_time,pay_valid_time, token_num,old_token_num, total_cvnt_pirce,unit_cvnt_price,total_usdt_price, unit_usdt_price, seller_uid "+
		" FROM mg_token_sale_order WHERE order_no = ?", orderNo)
	if err != nil {
		Log("err", err.Error())
	}
	return
}

//生成令牌
//Auth:Xu
func InsertMgToken(db sqlx.Execer, uid int64) (id int64, err error) {
	res, err := db.Exec("INSERT INTO mg_token (belong_uid, use_uid, create_time) values(?,?,?)", uid, uid, time.Now().Unix())
	if err != nil {
		Log("err", err.Error())
		return
	}
	id, _ = res.LastInsertId()

	return
}

//生成令牌流水
//Auth:Xu
func InsertMgTokenLog(db sqlx.Execer, targetUid, sourceUid int64, tokenId int64, orderNo string, logType int) (err error) {
	_, err = db.Exec("INSERT INTO mg_token_log (token_id, source_uid,target_uid, order_no, `type`, create_time) values(?,?,?,?,?,?)", tokenId, sourceUid, targetUid, orderNo, logType, time.Now().Unix())
	if err != nil {
		Log("err", err.Error())
	}
	return
}

//根据订单号查询生成成功的令牌数量
//Auth:Xu
func GetSuccTokenNumByOrderNo(db sqlx.Queryer, orderNo string) (codeNum int, err error) {
	err = sqlx.Get(db, &codeNum, "SELECT count(*) FROM mg_token_log WHERE order_no = ?", orderNo)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", err.Error())
		return
	}
	return
}

//官方渠道购买令牌，已全部生成完，修改complete_time
//Auth:Xu
func UpdateTokenSaleOrderPlatformBuy(db sqlx.Execer, orderNo string) (err error) {
	var rowId int64
	r, err := db.Exec("UPDATE mg_token_sale_order set  complete_time = ?  where order_no = ? ", time.Now().Unix(), orderNo)
	if err == nil {
		rowId, err = r.RowsAffected()
	} else {
		Log("err", err.Error())
		rowId = 0
	}

	if rowId <= 0 {
		return errno.DbError
	}
	return
}

//确认、支付时间超时，修改订单状态
//Auth:Xu
func UpdateTokenSaleOrderStatus(db sqlx.Execer, orderNo string, status int) (err error) {
	_, err = db.Exec("UPDATE mg_token_sale_order set  status = ?  where order_no = ? ", status, orderNo)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//修改令牌回购队列记录的状态
//Auth:Xu
func UpdateTokenDrawQueueStatus(db sqlx.Execer, orderNo string, status int) (err error) {
	_, err = db.Exec("UPDATE mg_token_draw_queue set  status = ?  where order_no = ? ", status, orderNo)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//查询全网令牌总的排队数量
//Auth:Xu
func GetBuyBackQueueNum(db sqlx.Queryer) (num int, err error) {
	err = sqlx.Get(db, &num, "SELECT IFNULL(sum(token_num),0) num FROM mg_token_draw_queue WHERE status = ? or status = ?", TokenQueueStatusRankLocking, TokenQueueStatusRanking)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", err.Error())
		return
	}
	return
}

//查询全网令牌总的排队数量2
//Auth:Xu
func GetBuyBackQueueNum2(db sqlx.Queryer) (num int, err error) {
	err = sqlx.Get(db, &num, "SELECT IFNULL(sum(token_num),0) num FROM mg_token_draw_queue WHERE (status = ? or status = ?) and uid != 100000000 ", TokenQueueStatusRankLocking, TokenQueueStatusRanking)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", err.Error())
		return
	}
	return
}

//查询全网今日令牌总回购数量
//Auth:Xu
func GetTodayBuyBackNum(db sqlx.Queryer) (num int, err error) {
	err = sqlx.Get(db, &num, "SELECT IFNULL(sum(token_num),0) num FROM mg_token_sale_order WHERE (status = ? or status = ?) and TO_DAYS(FROM_UNIXTIME(complete_time)) = TO_DAYS(now()) and type = ?", TokenSaleOrderStatusSale, TokenSaleOrderStatusComplete, TokenSaleOrderPlatBuyback)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", err.Error())
		return
	}
	return
}

//查询全网今日令牌总回购数量2
//Auth:Xu
func GetTodayBuyBackNum2(db sqlx.Queryer) (num int, err error) {
	err = sqlx.Get(db, &num, "SELECT IFNULL(sum(token_num),0) num FROM mg_token_sale_order WHERE "+
		" (status = ? or status = ?) and seller_uid != 100000000 and TO_DAYS(FROM_UNIXTIME(complete_time)) "+
		" = TO_DAYS(now()) and type = ?", TokenSaleOrderStatusSale, TokenSaleOrderStatusComplete, TokenSaleOrderPlatBuyback)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", err.Error())
		return
	}
	return
}

//用户节点数(质押了令牌)
//pledged 0未质押的节点数  1挖矿的节点数
func GetUserNodeNumWithType(uid int64, Type int, pledged int) (num int, err error) {
	sqlStr := fmt.Sprintf("SELECT COUNT(*) FROM mg_node WHERE uid = %d AND status != 2 AND type = %d", uid, Type)
	if pledged == 0 {
		sqlStr += " AND token_status = 1 "
	} else {
		sqlStr += " AND token_status = 20 "
	}
	err = sqlx.Get(config.C.DB.DB, &num, sqlStr)
	if err != nil {
		Log("err", "失败 GetUserNodeNumWithType: ", err.Error())
	}
	return
}

type UserNodeNum struct {
	Uid       int64 `db:"uid"`
	PcNum     int   `db:"pcNum"`
	LinuxNum  int   `db:"linuxNum"`
	RouterNum int   `db:"routerNum"`
}

//用户挖矿节点数
func GetUserPledgedNodeNum(uid int64) (nodeNum UserNodeNum, err error) {
	sqlStr := fmt.Sprintf("SELECT IFNULL(SUM(IF(type = 1, 1, 0)), 0) AS pcNum, IFNULL(SUM(IF(type = 2, 1, 0)), 0) AS linuxNum,"+
		" IFNULL(SUM(IF(type = 3, 1, 0)), 0) AS routerNum "+
		" FROM mg_node WHERE uid = %d AND token_status = 20 AND status != 2", uid)
	err = sqlx.Get(config.C.DB.DB, &nodeNum, sqlStr)
	if err != nil {
		Log("err", "失败 GetUserPledgedNodeNum：", err.Error())
	}
	return
}

//超级节点旗下所有用户的节点数
//pledged 0绑定的节点数  1挖矿的节点数
func GetSuperNodeAllUserNodeNum(superNodeId int64, pledged int) (info []UserNodeNum, err error) {
	sqlStr := fmt.Sprintf("SELECT node.uid, IFNULL(SUM(IF(node.type = 1, 1, 0)), 0) AS pcNum, IFNULL(SUM(IF(node.type = 2, 1, 0)), 0) AS linuxNum, IFNULL(SUM(IF(node.type = 3, 1, 0)), 0) AS routerNum"+
		" FROM mg_node node LEFT JOIN super_node_miner miner ON node.uid = miner.uid WHERE miner.super_node_id = %d AND node.status != 2", superNodeId)
	if pledged == 1 {
		sqlStr += " AND node.token_status > 1"
	}
	sqlStr += " GROUP BY node.uid ORDER BY miner.top_status DESC, miner.join_time DESC, node.uid"
	err = sqlx.Select(config.C.DB.DB, &info, sqlStr)
	if err != nil {
		Log("err", "失败 GetSuperNodeAllUserNodeNum：", err.Error())
	}
	return
}

//查询用户质押的节点数（除了路由器）
func GetUserPledgedTokenNodeNumExceptRouter(uid int64) (num int, err error) {
	err = sqlx.Get(config.C.DB.DB, &num, "SELECT COUNT(*) FROM mg_node WHERE uid = ? AND token_status != 1 AND status != 2 AND type != ?", uid, NodeTypeRouter)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetUserPledgedTokenNodeNumExceptRouter：", err.Error())
	}
	return
}

type NodeInfo struct {
	NodeId       int    `json:"node_id" db:"node_id"`
	Status       int    `json:"status" db:"token_status"`
	MiningStatus int    `json:"miningStatus" db:"status"`
	Name         string `json:"name" db:"name"`
	TokenId      int    `json:"tokenId"`
	TokenType    int    `json:"-"`
	NodeType     int    `json:"-" db:"type"`
	NodeNum      int    `json:"-" db:"node_num"`
}

//获取节点列表
func GetNodeList(uid int64, Type int, per, page int) (list []NodeInfo, err error) {
	start := (page - 1) * per
	if Type == 0 {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT node_id, name, status, token_status, storage, online_time"+
			" FROM mg_node WHERE uid = ? AND status != 2 AND token_status NOT in (2, 3) ORDER BY node_id DESC LIMIT ?, ?", uid, start, per)
	} else {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT node_id, name, status, token_status FROM mg_node WHERE "+
			" uid = ? AND type = ? AND status != 2 AND token_status NOT in (2, 3) ORDER BY node_id DESC LIMIT ?, ?", uid, Type, start, per)
	}
	if err != nil {
		Log("err", "失败 GetNodeList：", err.Error())
	}
	return
}

//获取用户新版所有节点列表
type UserNodeInfo struct {
	NodeId      int64  `json:"node_id" db:"node_id"`
	Name        string `json:"name" db:"name"`
	Status      int    `json:"status" db:"status"`
	TokenStatus int    `json:"token_status" db:"token_status"`
	Storage     int    `json:"storage" db:"storage"`
	OnlineTime  int    `json:"online" db:"online_time"`
	Type        int    `json:"type" db:"type"`
	Process     int    `json:"process" db:"process"`
	Offline     int    `json:"offline" db:"offline"`
	Up          int64  `json:"flow" db:"up"`
	PublicIp    string `db:"public_ip" json:"public_ip"`
}

func GetUserNodeList(uid int64, per, page int) (list []UserNodeInfo, err error) {
	start := (page - 1) * per
	err = sqlx.Select(config.C.DB.DB, &list, "SELECT node_id, IFNULL(public_ip, '') as public_ip, name, status, "+
		" IFNULL(token_status, 1) as token_status, IFNULL(storage, 1) as storage, "+
		" IF( online_time<60 && online_time >= 1, 1, FLOOR(IFNULL(online_time, 0)/60) ) as online_time, "+
		" type, IFNULL(process, 0) as process, IFNULL(offline, 0) as offline FROM mg_node WHERE uid = ? "+
		" AND status != 2 AND `type` = 2 ORDER BY node_id DESC LIMIT ?, ?", uid, start, per)
	if err != nil {
		Log("err", "失败 GetUserNodeList：", err.Error())
	}
	return
}

// 获取指定nodeId List 对应的今天流量
// Auth: hzp
type NodeListFlows struct {
	NodeId int64 `json:"node_id" db:"node_id,omitempty"`
	Up     int64 `json:"flow" db:"up"`
}

func GetUserNodeListFlowsByNodeList(Uid int64) (res []NodeListFlows, err error) {
	err = sqlx.Select(config.C.DB.DB, &res, "SELECT IFNULL(node_id, 0) AS node_id, IFNULL(SUM(up), 0) AS up"+
		" FROM mg_node_flow where `time` >= UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE)) AND node_id in "+
		" (SELECT node_id FROM mg_node WHERE uid= ?) GROUP BY node_id ", Uid)
	if err != nil {
		return res, err
	}
	return res, nil
}

//获取某令牌下的所有节点
func GetNodeListWithTokenId(tokenId int, uid int64) (list []NodeInfo, err error) {
	err = sqlx.Select(config.C.DB.DB, &list, "SELECT node_id, name FROM mg_node WHERE token_id = ? AND uid = ? "+
		" AND token_status != 1 ORDER BY node_id DESC", tokenId, uid)
	if err != nil {
		Log("err", "失败 GetNodeListWithTokenId：", err.Error())
	}
	return
}

//获取节点质押信息
func GetNodePledgeInfoByNodeId(nodeId int) (node NodeInfo, err error) {
	err = sqlx.Get(config.C.DB.DB, &node, "SELECT type, token_status FROM mg_node WHERE node_id = ?", nodeId)
	if err != nil {
		Log("err", "失败 GetNodePledgeInfoByNodeId：", err.Error())
	}
	return
}

//所有质押/未质押的节点
//source 0 未质押的   1质押的
func GetAllNode(uid int64, Type int, source, limit int) (list []NodeInfo, err error) {
	if source == 0 {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT node_id, token_status FROM mg_node WHERE uid = ? AND type = ? AND status != 2 AND token_status = 1 ORDER BY node_id DESC LIMIT ?", uid, Type, limit)
	} else {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT node_id, token_status FROM mg_node WHERE uid = ? AND type = ? AND status != 2 AND token_status != 1 ORDER BY node_id DESC LIMIT ?", uid, Type, limit)
	}
	if err != nil {
		Log("err", "失败 GetAllNode：", err.Error())
	}
	return
}

type SaleOrderInfo struct {
	OrderNo    string `json:"orderNo" db:"order_no"`
	Time       string `json:"time" db:"time"`
	Name       string `json:"name" db:"name"`
	Price      string `json:"price" db:"price"`
	TotalPrice string `json:"totalPrice" db:"totalPrice"`
	Num        int    `json:"num" db:"num"`
}

/*
 * @ Author: hupfei
 * @ Description: 购买/出售订单  source 0购买  1出售
 **/
func GetSaleOrderList(uid int64, source int, per, page int) (list []SaleOrderInfo, err error) {
	start := (page - 1) * per
	if source == 0 {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT order_no, IF(type = 1, CONCAT(round(unit_cvnt_price,2), \"CVNT\"), CONCAT(round(unit_usdt_price,2), \"USDT\")) AS price, "+
			"IF(type = 1, CONCAT(round(total_cvnt_pirce,2), \"CVNT\"), CONCAT(round(total_usdt_price,2), \"USDT\")) AS totalPrice, deal_num AS num, FROM_UNIXTIME(complete_time,'%Y-%m-%d %H:%i:%s') AS time, "+
			"IF(type = 1, '官方', '第三方') AS name "+
			"FROM mg_token_sale_order WHERE (type = 1 OR type = 3) AND status = 4 AND buyer_uid = ? "+
			"ORDER BY complete_time DESC, id DESC LIMIT ?, ?", uid, start, per)
	} else {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT order_no, IF(type = 4, CONCAT(round(unit_cvnt_price,2), \"CVNT\"), CONCAT(round(unit_usdt_price,2), \"USDT\")) AS price, "+
			"IF(type = 4, CONCAT(round(total_cvnt_pirce,2), \"CVNT\"), CONCAT(round(total_usdt_price,2), \"USDT\")) AS totalPrice, deal_num AS num, "+
			"FROM_UNIXTIME(complete_time,'%Y-%m-%d %H:%i:%s') AS time, IF(type = 4, '官方', '第三方') AS name "+
			"FROM mg_token_sale_order WHERE (type = 3 OR type = 4) AND status = 4 AND seller_uid = ? "+
			"ORDER BY complete_time DESC, id DESC LIMIT ?, ?", uid, start, per)
	}
	if err != nil {
		Log("err", "失败 GetSaleOrderList：", err.Error())
	}
	return
}

/*
 * @ Author: hupfei
 * @ Description:借入/借出订单  source 0借入  1借出
 **/

type SellOrderInfo struct {
	OrderNo       string  `json:"orderNo" db:"order_no"`
	Time          string  `json:"time" db:"time"`
	Name          string  `json:"name" db:"name"`
	Num           int     `json:"num" db:"num"`
	RemainingTime int     `json:"remainingTime" db:"remainingTime"`
	Scale         float64 `json:"scale" db:"scale"`
	Status        string  `json:"status"`
	Uid           int64   `json:"uid" db:"uid"`
	Days          int64   `json:"days" db:"days"`
	OrderStatus   int     `json:"-" db:"orderStatus"`
}

func GetSellOrderList(uid int64, source int, per, page int) (list []SellOrderInfo, err error) {
	start := (page - 1) * per
	if source == 0 {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT order_no, deal_num AS num, o.status AS orderStatus, days, FROM_UNIXTIME(o.create_time,'%Y-%m-%d %H:%i:%s') AS time, "+
			"IF(end_time>UNIX_TIMESTAMP(NOW()), CEIL((end_time-UNIX_TIMESTAMP(NOW())) / (60*60*24)), 0) AS remainingTime, scale, u.nickName AS name, u.uid "+
			"FROM mg_token_sell_order o LEFT JOIN user u ON o.rent_uid = u.uid "+
			"WHERE lend_uid = ? AND (o.status = 2 OR o.status = 4 OR o.status = 5) AND type = 2 "+
			"ORDER BY o.status ASC, o.create_time DESC, id DESC LIMIT ?, ?", uid, start, per)
	} else {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT order_no, deal_num AS num, o.status AS orderStatus, FROM_UNIXTIME(o.create_time,'%Y-%m-%d %H:%i:%s') AS time, "+
			"IF(end_time>UNIX_TIMESTAMP(NOW()), CEIL((end_time-UNIX_TIMESTAMP(NOW())) / (60*60*24)), 0) AS remainingTime, scale, u.nickName AS name, u.uid "+
			"FROM mg_token_sell_order o LEFT JOIN user u ON o.lend_uid = u.uid "+
			"WHERE rent_uid = ? AND (o.status = 2 OR o.status = 4 OR o.status = 5) AND type = 2 "+
			"ORDER BY o.status ASC, o.create_time DESC, id DESC LIMIT ?, ?", uid, start, per)
	}
	if err != nil {
		Log("err", "失败 GetSellOrderList：", err.Error())
	}
	return
}

type TokenInfo struct {
	Id          int `json:"id" db:"id"`
	NodeNum     int `json:"nodeNum" db:"node_num"`
	TokenType   int `json:"-" db:"tokenType"`
	TokenStatus int `json:"tokenStatus" db:"token_status"`
}

//查询用户闲置的令牌(包含碎片令牌)
func GetUserIdleTokenList(uid int64, nodeType int, limit int) (list []TokenInfo, err error) {
	if nodeType == NodeTypeRouter {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT id, IF(rent_status=1,1,0) AS tokenType, node_num "+
			"FROM mg_token WHERE use_uid = ? AND (status = ? OR (status = ? AND node_num < ? AND type = ?)) "+
			"ORDER BY rent_status DESC, id DESC LIMIT ?", uid, TokenUsedStatusUnused, TokenUsedStatusMining, OneTokenCanBePledgedMaxNum, NodeTypeRouter, limit)
	} else {
		err = sqlx.Select(config.C.DB.DB, &list, "SELECT id, IF(rent_status=1,1,0) AS tokenType "+
			"FROM mg_token WHERE use_uid = ? AND status = ? "+
			"ORDER BY rent_status DESC, id DESC LIMIT ?", uid, TokenUsedStatusUnused, limit)
	}
	if err != nil {
		Log("err", "失败 GetUserIdleTokenList：", err.Error())
	}
	return
}

//查询路由器质押的令牌
func GetRouterTokenList(uid int64, per, page int) (list []TokenInfo, err error) {
	start := (page - 1) * per
	err = sqlx.Select(config.C.DB.DB, &list, "SELECT id, node_num, IF(rent_status = 1,2,3) AS token_status "+
		"FROM mg_token WHERE type = ? AND status = ? AND ((belong_uid = ? AND rent_status = ?) OR (use_uid = ? AND rent_status = ?)) "+
		"ORDER BY token_status DESC, node_num ASC, id DESC LIMIT ?, ?", NodeTypeRouter, TokenUsedStatusMining, uid, TokenRentStatusNormal, uid, TokenRentStatusRented, start, per)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetRouterTokenList：", err.Error())
	}
	return
}

type DepositLog struct {
	Cvnt       float64 `json:"cvnt" db:"cvn"`
	CreateTime string  `json:"createTime" db:"create_time"`
}

//获取保证金明细
func GetDepositLogList(uid int64, per, page int) (list []DepositLog, err error) {
	start := (page - 1) * per
	err = sqlx.Select(config.C.DB.DB, &list, "SELECT -amount AS cvn, FROM_UNIXTIME(create_time, '%Y-%m-%d %H:%i:%s') AS create_time "+
		"FROM cvn_log WHERE uid=? AND (source = ? OR source = ? OR source = ? OR source = ?) "+
		"ORDER BY id DESC LIMIT ?, ?", uid, LogSourceSystemAddDeposit, LogSourcePledgeDeposit, LogSourceDepositToken, LogSourceSystemDeductDeposit, start, per)
	if err != nil {
		Log("err", "失败 GetDepositLogList：", err.Error())
	}
	return
}

//获取一个令牌
func GetOneToken(uid int64, tokenType int) (token TokenInfo, err error) {
	if tokenType == 0 {
		err = sqlx.Get(config.C.DB.DB, &token, "SELECT MAX(id) AS id FROM mg_token WHERE use_uid = ? AND rent_status = 2 AND status = 1", uid)
	} else {
		err = sqlx.Get(config.C.DB.DB, &token, "SELECT MAX(id) AS id FROM mg_token WHERE belong_uid = ? AND rent_status = 1 AND status = 1", uid)
	}
	if err == sql.ErrNoRows {
		Log("err", "失败 GetOneToken：", uid, ":", tokenType)
		return token, errors.New("未找到闲置令牌")
	}
	if err != nil {
		Log("err", "失败 GetOneToken：", err.Error())
		return token, errno.DbSearchError
	}
	return
}

//获取node_num最大的令牌
func GetIncompleteTokenForMaxNodeNum(uid int64, tokenType int) (token TokenInfo, err error) {
	if tokenType == 0 {
		err = sqlx.Get(config.C.DB.DB, &token, "SELECT id FROM mg_token WHERE use_uid = ? AND status = ? AND rent_status = ? AND type = ? AND node_num > 0 AND node_num < ? "+
			"ORDER BY node_num DESC LIMIT 1", uid, TokenUsedStatusMining, TokenRentStatusRented, NodeTypeRouter, OneTokenCanBePledgedMaxNum)
	} else {
		err = sqlx.Get(config.C.DB.DB, &token, "SELECT id FROM mg_token WHERE belong_uid = ? AND status = ? AND rent_status = ? AND type = ? AND node_num > 0 AND node_num < ? "+
			"ORDER BY node_num DESC LIMIT 1", uid, TokenUsedStatusMining, TokenRentStatusNormal, NodeTypeRouter, OneTokenCanBePledgedMaxNum)
	}
	if err == sql.ErrNoRows {
		Log("err", "失败 GetMaxNodeNumToken：", uid, ":", tokenType)
		return token, errors.New("未找到闲置的碎片令牌")
	}
	if err != nil {
		Log("err", "失败 GetMaxNodeNumToken：", err.Error())
		return token, errno.DbSearchError
	}
	return
}

//根据节点id查询令牌id
func GetTokenIdByNodeId(uid int64, nodeId int) (node int, err error) {
	err = sqlx.Get(config.C.DB.DB, &node, "SELECT token_id FROM mg_node WHERE node_id = ? AND uid = ?", nodeId, uid)
	if err == sql.ErrNoRows {
		Log("err", "失败 GetTokenIdByNodeId：", nodeId)
		return node, errno.IllegalErr
	}
	if err != nil {
		Log("err", "失败 GetTokenIdByNodeId：", err.Error())
		return node, errno.DbSearchError
	}
	return
}

//质押一个节点
//tokenType 0租用令牌  1自有令牌
func SingelPledge(uid int64, nodeId, tokenId int) (err error) {
	Log("info", "质押令牌：uid-", uid, " nodeId-", nodeId, " tokenId-", tokenId)
	// 开启事务
	conn, err := config.C.DB.DB.Begin()
	if err != nil {
		Log("err", "质押令牌开启事务失败", err.Error())
		return errors.New("质押失败，请稍后重试")
	}

	//查找节点信息
	nodeType := 0
	nodeTokenStatus := 0
	err = conn.QueryRow("SELECT type, token_status FROM mg_node WHERE node_id = ?", nodeId).Scan(&nodeType, &nodeTokenStatus)
	if err != nil {
		return errno.DbSearchError
	}
	if nodeTokenStatus > NodePledgeStatusNormal {
		Log("err", "质押失败，此节点已经质押过, nodeTokenStatus = ", nodeTokenStatus, " nodeId = ", nodeId)
		return errors.New("质押失败，节点状态错误")
	}

	//令牌信息
	tokenRentStatus := 0
	tokenUsedStatus := 0
	nodeNum := 0
	err = conn.QueryRow("SELECT rent_status, `status`, node_num FROM mg_token WHERE id = ?", tokenId).Scan(&tokenRentStatus, &tokenUsedStatus, &nodeNum)
	if err != nil {
		return errno.DbSearchError
	}
	if nodeType == NodeTypeRouter {
		if nodeNum > OneTokenCanBePledgedMaxNum || (tokenUsedStatus != TokenUsedStatusUnused && tokenUsedStatus != TokenUsedStatusMining) {
			Log("err", "质押失败，此令牌状态错误(路由器), nodeNum = ", nodeNum, " tokenUsedStatus = ", tokenUsedStatus, " tokenId = ", tokenId)
			return errors.New("质押失败，令牌状态错误")
		}
	} else {
		if nodeNum >= 1 || tokenUsedStatus != TokenUsedStatusUnused {
			Log("err", "质押失败，此令牌状态错误, nodeNum = ", nodeNum, " tokenUsedStatus = ", tokenUsedStatus, " tokenId = ", tokenId)
			return errors.New("质押失败，令牌状态错误")
		}
	}

	var rowId1, rowId2, rowId3 int64
	//更新mg_token表
	r1, err1 := conn.Exec("UPDATE mg_token SET status = 2, node_num = node_num + 1, type = ? WHERE id = ?", nodeType, tokenId)
	if err1 == nil {
		rowId1, err1 = r1.RowsAffected()
		if err1 != nil {
			Log("err", "SingelPledge 更新mg_token表失败(RowsAffected)，tokenId = ", tokenId, err1.Error())
			rowId1 = 0
		}
	} else {
		Log("err", "SingelPledge 更新mg_token表失败，tokenId = ", tokenId, err1.Error())
	}

	// 更新mg_node
	r2, err2 := conn.Exec("UPDATE mg_node SET token_status = 20, token_id = ?, pledge_time = UNIX_TIMESTAMP(now()) "+
		" WHERE node_id = ? AND uid = ? AND type = ? AND status != 2", tokenId, nodeId, uid, nodeType)
	if err2 == nil {
		rowId2, err2 = r2.RowsAffected()
		if err2 != nil {
			Log("err", "SingelPledge 更新mg_node表失败(RowsAffected)，nodeId = ", nodeId, err2.Error())
			rowId2 = 0
		}
	} else {
		Log("err", "SingelPledge 更新mg_node表失败，nodeId = ", nodeId, err2.Error())
	}

	//增加日志
	r3, err3 := conn.Exec("INSERT INTO mg_token_log SET source_uid = ?, token_id = ?, node_id = ?, type = ?, create_time = UNIX_TIMESTAMP(now())", uid, tokenId, nodeId, TokenLogTypePledge)
	if err3 == nil {
		rowId3, err3 = r3.RowsAffected()
		if err3 != nil {
			Log("err", "SingelPledge 更新日志表失败(RowsAffected)，tokenId = ", tokenId, err3.Error())
			rowId3 = 0
		}
	} else {
		Log("err", "SingelPledge 更新日志表失败，tokenId = ", tokenId, err3.Error())
	}

	// 提交事务
	if rowId1 > 0 && rowId2 > 0 && rowId3 > 0 {
		err = conn.Commit()
		if err != nil {
			Log("err", "SingelPledge 事务提交失败", err.Error())
			err = conn.Rollback()
			if err != nil {
				Log("err", "SingelPledge 事务回滚失败", err.Error())
			}
			return errors.New("质押失败，请稍后重试")
		}
		Log("info", "质押令牌成功：uid-", uid, " nodeId-", nodeId, " tokenId-", tokenId)
		return nil
	} else {
		Log("err", "质押失败 rowId1:", rowId1, ":rowId2:", rowId2, ":rowId3:", rowId3)
		err = conn.Rollback()
		if err != nil {
			Log("err", "SingelPledge 事务回滚失败", err.Error())
		}
		return errors.New("质押失败")
	}
}

//取消单个质押
func CancelSingelPledge(uid int64, nodeId, tokenId int) (err error) {

	Log("info", "释放令牌：uid-", uid, " nodeId-", nodeId, " tokenId-", tokenId)
	// 开启事务
	conn, err := config.C.DB.DB.Begin()
	if err != nil {
		return err
	}

	var rowId1, rowId2, rowId3 int64
	//更新mg_token表
	r1, err1 := conn.Exec("UPDATE mg_token SET status = IF(type=3&&node_num>1,status, 1), type = IF(type=3&&node_num>1,type, 0), node_num = node_num-1 WHERE id = ?", tokenId)
	if err1 == nil {
		rowId1, err1 = r1.RowsAffected()
		if err1 != nil {
			Log("err", "CancelSingelPledge 更新mg_token表失败(RowsAffected)，tokenId = ", tokenId, err1.Error())
			rowId1 = 0
		}
	} else {
		Log("err", "CancelSingelPledge 更新mg_token表失败，tokenId = ", tokenId, err1.Error())
	}

	// 更新mg_node
	r2, err2 := conn.Exec("UPDATE mg_node SET token_status = 1, token_id = 0, pledge_time = 0 WHERE node_id = ? AND uid = ? AND status != 2", nodeId, uid)
	if err2 == nil {
		rowId2, err2 = r2.RowsAffected()
		if err2 != nil {
			Log("err", "CancelSingelPledge 更新mg_node表失败，tokenId = ", tokenId, err2.Error())
			rowId2 = 0
		}
	} else {
		Log("err", "CancelSingelPledge 更新mg_node表失败(RowsAffected)，tokenId = ", tokenId, err2.Error())
	}

	//增加日志
	r3, err3 := conn.Exec("INSERT INTO mg_token_log SET source_uid = ?, token_id = ?, node_id = ?, type = ?, create_time = UNIX_TIMESTAMP(now())", uid, tokenId, nodeId, TokenLogTypeCancelPledge)
	if err3 == nil {
		rowId3, err3 = r3.RowsAffected()
		if err3 != nil {
			Log("err", "CancelSingelPledge 更新日志表失败(RowsAffected)，tokenId = ", tokenId, err3.Error())
			rowId3 = 0
		}
	} else {
		Log("err", "CancelSingelPledge 更新日志表失败，tokenId = ", tokenId, err3.Error())
	}

	// 提交事务
	if rowId1 > 0 && rowId2 > 0 && rowId3 > 0 {
		err = conn.Commit()
		if err != nil {
			Log("err", "CancelSingelPledge 事务提交失败", err.Error())
			err = conn.Rollback()
			if err != nil {
				Log("err", "CancelSingelPledge 事务回滚失败", err.Error())
			}
			return errors.New("撤销质押失败，请稍后重试")
		}
		return nil
	} else {
		Log("err", "CancelSingelPledge rowId1:", rowId1, ":rowId2:", rowId2, ":rowId3:", rowId3)
		err = conn.Rollback()
		if err != nil {
			Log("err", "CancelSingelPledge 事务回滚失败", err.Error())
		}
		return errors.New("撤销质押失败")
	}
}

//批量释放令牌
func BatchCancelPledge(uid int64, Type int) bool {
	Log("info", "BatchCancelPledge批量释放令牌：uid-", uid)

	// 开启事务
	tx, err := config.C.DB.DB.Begin()
	if err != nil {
		Log("err", "BatchCancelPledge事务开启失败：", err.Error())
		return false
	}

	//更新mg_token表
	res, err := tx.Exec("UPDATE mg_token SET status = 1, type = 0, node_num = 0 WHERE use_uid = ? AND type = ?", uid, Type)
	if err != nil {
		Log("err", "更新mg_token失败：", err.Error())
		err := tx.Rollback()
		if err != nil {
			Log("err", "更新mg_token失败后回滚失败：", err.Error())
		}
		return false
	}
	row, err := res.RowsAffected()
	if err != nil {
		Log("err", "获取更新mg_token影响的行数失败：", err.Error())
		err := tx.Rollback()
		if err != nil {
			Log("err", "获取更新mg_token影响的行数失败后回滚失败：", err.Error())
		}
		return false
	}
	Log("info", "更新mg_token影响行数为：", row)

	// 更新mg_node
	res, err = tx.Exec("UPDATE mg_node SET token_status = 1, token_id = 0, pledge_time = 0 WHERE uid = ? AND type = ?", uid, Type)
	if err != nil {
		Log("err", "更新mg_node失败：", err.Error())
		err := tx.Rollback()
		if err != nil {
			Log("err", "更新mg_node失败后回滚失败：", err.Error())
		}
		return false
	}
	row, err = res.RowsAffected()
	if err != nil {
		Log("err", "获取更新mg_node影响的行数失败：", err.Error())
		err := tx.Rollback()
		if err != nil {
			Log("err", "获取更新mg_node影响的行数失败后回滚失败：", err.Error())
		}
		return false
	}
	Log("info", "更新mg_node影响行数为：", row)

	//增加日志
	res, err = tx.Exec("INSERT INTO mg_token_log SET source_uid = ?, token_id = ?, type = ?, create_time = UNIX_TIMESTAMP(now())", uid, Type, TokenLogTypeBatchCancelPledge)
	if err != nil {
		Log("err", "新增mg_token_log失败：", err.Error())
		err := tx.Rollback()
		if err != nil {
			Log("err", "新增mg_token_log失败后回滚失败：", err.Error())
		}
		return false
	}
	id, err := res.LastInsertId()
	if err != nil {
		Log("err", "新增mg_token_log后获取id失败：", err.Error())
		err := tx.Rollback()
		if err != nil {
			Log("err", "新增mg_token_log后获取id失败后回滚失败：", err.Error())
		}
		return false
	}
	if id <= 0 {
		Log("err", "新增mg_token_log的id异常,id=：", id)
		err := tx.Rollback()
		if err != nil {
			Log("err", "新增mg_token_log的id异常后回滚失败：", err.Error())
		}
		return false
	}

	err = tx.Commit()
	if err != nil {
		Log("err", "BatchCancelPledge提交事务失败：", err.Error())
		err := tx.Rollback()
		if err != nil {
			Log("err", "BatchCancelPledge提交事务失败后回滚失败：", err.Error())
		}
		return false
	}

	return true
}

//释放所有令牌
func ReleaseAllToken(tx sqlx.Execer, uid int64) bool {
	res, err := tx.Exec("UPDATE mg_node SET token_status = 1, token_id = 0, pledge_time = 0 WHERE uid = ?", uid)
	if err != nil {
		Log("err", "更新mg_node失败：", err.Error())
		return false
	}
	row, err := res.RowsAffected()
	if err != nil {
		Log("err", "获取更新mg_node影响的行数失败：", err.Error())
		return false
	}
	Log("info", "更新mg_node影响行数为：", row)

	res, err = tx.Exec("UPDATE mg_token SET status = 1, node_num = 0 WHERE use_uid = ?", uid)
	if err != nil {
		Log("err", "更新mg_token失败：", err.Error())
		return false
	}
	row, err = res.RowsAffected()
	if err != nil {
		Log("err", "获取更新mg_token影响的行数失败：", err.Error())
		return false
	}
	Log("info", "更新mg_token影响行数为：", row)

	//增加日志
	_, err = tx.Exec("INSERT INTO mg_token_log SET source_uid = ?, token_id = ?, type = ?, create_time = UNIX_TIMESTAMP(now())", uid, 0, TokenLogTypeBatchCancelPledge)
	if err != nil {
		Log("err", "新增mg_token_log失败：", err.Error())
		return false
	}

	return true
}

type TokenNum struct {
	New int `db:"new"`
	Old int `db:"old"`
}

//查询自有闲置令牌数量
//Auth:Xu
func GetMyselfTokenUnusedNum(conn *sql.Tx, uid int64) (tokenNum TokenNum, err error) {
	err = sqlx.Get(config.C.DB.DB, &tokenNum, "SELECT IFNULL(sum(if(batch=99,0,1)),0) new,IFNULL(sum(if(batch!=99,0,1)),0) old FROM mg_token WHERE belong_uid = ? and rent_status = 1 and status = ? ", uid, TokenUsedStatusUnused)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetMyselfTokenUnusedNum：", err.Error())
	}
	return
}

//查询自有锁定中的令牌数量是否足够
//Auth:Xu
func GetMyselfTokenLockingNum(db sqlx.Queryer, uid int64, num int, orderType int) (nums []int64, err error) {
	if orderType == TokenSaleOrderPlatBuyback {
		err = sqlx.Select(db, &nums, "SELECT id FROM mg_token WHERE belong_uid = ? and rent_status = 1 and status = ? order by batch desc limit ?", uid, TokenUsedStatusBuybackLocking, num)
	} else {
		err = sqlx.Select(db, &nums, "SELECT id FROM mg_token WHERE belong_uid = ? and rent_status = 1 and status = ? and batch != 99 limit ?", uid, TokenUsedStatusLocking, num)
	}

	if err != nil {
		Log("err", err.Error())
		return
	}
	return
}

//修改锁定中的令牌为  闲置状态(新旧令牌混合)
//Auth:Xu
func UpdateNewOldTokenToUnused(conn sqlx.Execer, uid int64, num, newNum, orderType int) (err error) {
	var rowNum int64
	if orderType == TokenSaleOrderPlatBuyback {
		res, err1 := conn.Exec("UPDATE `mg_token` SET `status` = ? WHERE belong_uid = ? and rent_status = 1 and  status = ? and batch = 99  limit ? ",
			TokenUsedStatusUnused, uid, TokenUsedStatusBuybackLocking, num)
		err = err1
		if err != nil {
			return
		}
		rowNum1, _ := res.RowsAffected()
		res2, err1 := conn.Exec("UPDATE `mg_token` SET `status` = ? WHERE belong_uid = ? and rent_status = 1 and  status = ? and batch != 99  limit ? ",
			TokenUsedStatusUnused, uid, TokenUsedStatusBuybackLocking, newNum)
		err = err1
		if err != nil {
			return
		}
		rowNum2, _ := res2.RowsAffected()
		rowNum = rowNum1 + rowNum2
	} else {
		res, err1 := conn.Exec("UPDATE `mg_token` SET `status` = ? WHERE belong_uid = ? and rent_status = 1 and  status = ? and batch != 99 limit ? ",
			TokenUsedStatusUnused, uid, TokenUsedStatusLocking, num)
		err = err1
		if err != nil {
			return
		}
		rowNum, err = res.RowsAffected()
	}
	if rowNum != int64(num+newNum) {
		Log("err", "UpdateNewOldTokenToUnused,"+strconv.FormatInt(uid, 10)+" 锁定令牌数异常"+strconv.FormatInt(rowNum, 10)+"_"+strconv.Itoa(num))
		return errors.New("闲置令牌数异常")
	}

	return
}

//出售、回购订单，锁定令牌
//Auth:Xu
func UpdateTokenToLock(conn sqlx.Execer, uid int64, num int, orderType int) (err error) {
	var rowNum int64
	//第三方出售，不包含旧令牌
	if orderType == TokenSaleOrderOutSell {
		res, err1 := conn.Exec("UPDATE `mg_token` SET `status` = ? WHERE belong_uid = ? and rent_status = 1 and status = ? and batch != 99 limit ? ",
			TokenUsedStatusLocking, uid, TokenUsedStatusUnused, num)
		rowNum, err = res.RowsAffected()
		err = err1
	} else {
		//官方回购, 优先旧令牌
		res, err2 := conn.Exec("UPDATE `mg_token` SET `status` = ? WHERE belong_uid = ? and rent_status = 1 and status = ? order by batch desc limit ? ",
			TokenUsedStatusBuybackLocking, uid, TokenUsedStatusUnused, num)
		rowNum, err = res.RowsAffected()
		err = err2
	}
	if err != nil {
		return
	}
	if rowNum != int64(num) {
		Log("err", "UpdateTokenToLock, "+strconv.FormatInt(uid, 10)+"闲置令牌数异常"+strconv.FormatInt(rowNum, 10)+"_"+strconv.Itoa(num))
		return errors.New("闲置令牌数异常")
	}

	return
}

//更新令牌为  已回收
//Auth:Xu
func UpdateTokenToRecycle(conn sqlx.Execer, uid int64, tokenIds []int64) (rowNums int64, err error) {
	query, args, err := sqlx.In("UPDATE `mg_token` SET `status` = ?, belong_uid = 0, use_uid = 0 WHERE status = ? and belong_uid = ? and rent_status = 1 and id in (?) ",
		TokenUsedStatusRecycle, TokenUsedStatusBuybackLocking, uid, tokenIds)

	query = config.C.DB.DB.Rebind(query)
	res, err := conn.Exec(query, args...)
	rowNums, _ = res.RowsAffected()
	if err != nil {
		return
	}

	return
}

//令牌出售，更新令牌信息()归属人UID
//Auth:Xu
func UpdateTokenBelong(conn sqlx.Execer, uid int64, tokenIds []int64) (rowNums int64, err error) {
	query, args, err := sqlx.In("UPDATE `mg_token` SET `status` = ?,belong_uid = ?, use_uid = ?  WHERE status = ? and  id in (?) ",
		TokenUsedStatusUnused, uid, uid, TokenUsedStatusLocking, tokenIds)

	query = config.C.DB.DB.Rebind(query)
	res, err := conn.Exec(query, args...)
	rowNums, _ = res.RowsAffected()
	if err != nil {
		return
	}

	return
}

//查询支付超时的订单
//Auth: Xu
func SelectOverdueTokenOrder(db sqlx.Queryer) (orders []TokenSaleOrder, err error) {
	err = sqlx.Select(db, &orders, "SELECT token_num,old_token_num, order_no,status, seller_uid, buyer_uid ,type,parent_order_no FROM mg_token_sale_order WHERE (status = 1 or status = 2 or status = 6) and pay_valid_time < ? and pay_status = ?", time.Now().Unix(), TokenOrderUnPay)
	if err != nil {
		Log("err", err.Error())
		return
	}
	return
}

//令牌回购订单提交，修改订单信息
//Auth:Xu
func UpdateTokenSaleOrderBuybackComplete(db sqlx.Execer, orderNo string, num int) (err error) {
	_, err = db.Exec("UPDATE mg_token_sale_order set  status = ?,deal_num = ?, complete_time = ?, pay_status = ?  where order_no = ? ", TokenSaleOrderStatusComplete, num, time.Now().Unix(), TokenOrderPayed, orderNo)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//令牌回购排队订单确认提交，修改订单信息
//Auth:Xu
func UpdateTokenSaleOrderBuybackRankConfirm(db sqlx.Execer, orderNo string) (err error) {
	_, err = db.Exec("UPDATE mg_token_sale_order set  pay_status = ?, pay_valid_time = ?   where order_no = ? ", TokenOrderPayed, time.Now().Unix(), orderNo)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//查询用户当前挂售的USDT订单数
//Auth：Xu
func GetCurrentUsdtOrderNumSelf(uid int64) (num int, err error) {
	err = sqlx.Get(config.C.DB.DB, &num, "SELECT count(*) FROM mg_token_sale_order WHERE seller_uid = ? AND status = ? AND type = ? ", uid, TokenSaleOrderStatusSale, TokenSaleOrderOutSell)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetCurrentUsdtOrderNum：", err.Error())
	}
	return
}

//查询用户当日已回购的令牌数
//Auth：Xu
func GetUserTodayBuybackTokenNum(uid int64) (num int, err error) {
	err = sqlx.Get(config.C.DB.DB, &num, "SELECT IFNULL(sum(token_num),0) FROM mg_token_sale_order WHERE seller_uid = ? AND (status = ? or status = ? or status = ?) AND type = ? AND TO_DAYS(FROM_UNIXTIME(create_time)) = TO_DAYS(now())", uid, TokenSaleOrderStatusSale, TokenSaleOrderStatusRank, TokenSaleOrderStatusComplete, TokenSaleOrderPlatBuyback)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetCurrentUsdtOrderNum：", err.Error())
	}
	return
}

//查询用户今日挂售的令牌订单数
//Auth：Xu
func GetTodayUsdtOrderNumSelf(uid int64) (num int, err error) {
	err = sqlx.Get(config.C.DB.DB, &num, "SELECT count(*) FROM mg_token_sale_order WHERE seller_uid = ? AND type = ? AND date_format(from_unixtime(create_time),'%Y-%m-%d') = date_format(now(),'%Y-%m-%d') ", uid, TokenSaleOrderOutSell)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetTodayUsdtOrderNumSelf：", err.Error())
	}
	return
}

//查询平台当前挂售的所有令牌订单
//Auth：Xu
func GetTokenUsdtOrderList(per, page int) (orders []TokenSaleOrder, err error) {
	start := (page - 1) * per
	err = sqlx.Select(config.C.DB.DB, &orders, "SELECT order_no, token_num, deal_num, lock_num, total_usdt_price, unit_usdt_price FROM mg_token_sale_order WHERE status = ? AND type = ? and token_num - deal_num - lock_num > 0 order by unit_usdt_price LIMIT ?, ?",
		TokenSaleOrderStatusSale, TokenSaleOrderOutSell, start, per)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "失败 GetTokenUsdtOrderList：", err.Error())
	}
	return
}

// 获取已完成租借令牌数
// Auth:hzp
func GetMgTokenInfoByUserId(uid int64) (count int, err error) {

	err = sqlx.Get(config.C.DB.DB, &count, "SELECT COUNT(*) FROM mg_token WHERE use_uid = ? AND `rent_status` = 2", uid)
	if err != nil {
		return 0, err
	}
	if count <= 0 {
		return 0, nil
	}
	return count, nil
}

// 获取 求租中 令牌数
// Auth:hzp
func GetMgTokenSellerOrderByUserId(uid int64) (res int, err error) {

	var count int
	err = sqlx.Get(config.C.DB.DB, &count, "SELECT COUNT(*) FROM mg_token_sell_order WHERE lend_uid = ? "+
		" AND `status`=1 and `type`=1", uid)
	if err != nil {
		return 0, err
	}
	if count <= 0 {
		return 0, nil
	}

	err = sqlx.Get(config.C.DB.DB, &res, "SELECT SUM(token_num - deal_num) FROM mg_token_sell_order WHERE "+
		" lend_uid = ? AND `status`=1 and `type`=1", uid)
	if err != nil {
		Log("error", "GetMgTokenSellerOrderByUserId 失败 ", uid, err.Error())
	}
	return res, nil
}

//锁定、释放锁定销售订单的令牌数
//Auth:Xu
func UpdateSaleOrderLockNum(conn sqlx.Execer, orderNo string, num int) (err error) {
	numAbs := math.Abs(float64(num))
	if num < 0 {
		_, err = conn.Exec("UPDATE mg_token_sale_order set  lock_num = lock_num - ?  where order_no = ? ", numAbs, orderNo)
	} else {
		_, err = conn.Exec("UPDATE mg_token_sale_order set  lock_num = lock_num + ?  where order_no = ? ", numAbs, orderNo)
	}
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//第三方购买订单完成
//Auth: Xu
func UpdateBuyOrderPayComplete(conn sqlx.Execer, orderNo string) (err error) {
	timeNow := time.Now().Unix()
	_, err = conn.Exec("UPDATE mg_token_sale_order set  pay_status = ?,status = ?, pay_time = ?, complete_time = ?, deal_num = token_num   where order_no = ? ", TokenOrderPayed, TokenSaleOrderStatusComplete, timeNow, timeNow, orderNo)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//第三方令牌销售订单完成、修改实际销售的令牌数、 释放锁定中的令牌书
//Auth: Xu
func UpdateSaleOrderComplete(conn sqlx.Execer, orderNo string, num int) (err error) {
	timeNow := time.Now().Unix()
	_, err = conn.Exec("UPDATE mg_token_sale_order set  status = ?,deal_num = deal_num + ?, lock_num = lock_num - ?, complete_time = ?   where order_no = ? ", TokenSaleOrderStatusComplete, num, num, timeNow, orderNo)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//第三方订单部分完成，修改父订单令牌锁定、完成数量
func UpdateSaleOrderDealNum(conn sqlx.Execer, orderNo string, num int) (err error) {
	_, err = conn.Exec("UPDATE mg_token_sale_order set  deal_num = deal_num + ?, lock_num = lock_num - ?   where order_no = ? ", num, num, orderNo)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//第三方令牌销售订单撤销
//Auth: Xu
func UpdateSaleOrderCancel(conn sqlx.Execer, orderNo string) (err error) {
	_, err = conn.Exec("UPDATE mg_token_sale_order set  status = ?, lock_num = 0 where order_no = ? ", TokenSaleOrderStatusCancel, orderNo)
	if err != nil {
		Log("err", err.Error())
	}

	return
}

//查询用户所有出售的订单（包含出售中的）
//Auth:Xu
func GetMySaleOrderList(uid int64, per, page int) (list []TokenSaleOrder, err error) {
	start := (page - 1) * per
	err = sqlx.Select(config.C.DB.DB, &list, "SELECT order_no, unit_usdt_price ,token_num, total_usdt_price , deal_num , create_time, status FROM mg_token_sale_order where seller_uid = ? and type = ?  ORDER BY status, create_time DESC, id DESC LIMIT ?, ?", uid, TokenSaleOrderOutSell, start, per)
	if err != nil {
		Log("err", "失败 GetMySaleOrderList：", err.Error())
	}
	return
}

type RentListInfo struct {
	OrderNo        string  `db:"orderNo" json:"orderNo"`
	LendUid        int64   `db:"lend_uid" json:"lend_uid"`
	Nickname       string  `db:"nickname, omitempty" json:"nickname, omitempty"`
	Num            int     `db:"num, omitempty" json:"num, omitempty"`
	TotalNum       int     `db:"totalNum, omitempty" json:"totalNum, omitempty"`
	RentedNum      int     `db:"deal_num, omitempty" json:"rentedNum, omitempty"`
	Days           int     `db:"days" json:"days"`
	Scale          float64 `db:"scale" json:"scale"`
	Info           string  `db:"info" json:"info"`
	AverageRevenue float64 `db:"averageRevenue, omitempty" json:"averageRevenue, omitempty"`
	CreateTime     string  `db:"createTime" json:"createTime"`
	IsLowIncome    int     `db:"isLowIncome" json:"isLowIncome"`
}

//查询发布中的租赁订单
//Auth:Xu
func GetRentOrderList(per, page int, average float64) (orderList []RentListInfo, err error) {
	start := (page - 1) * per
	err = sqlx.Select(config.C.DB.DB, &orderList, "select order_no as orderNo,lend_uid , u.nickname, m.token_num - m.deal_num num, days, scale, s.income_ability as averageRevenue, if(income_ability < ?,1,2) isLowIncome, from_unixtime(m.create_time, '%Y-%m-%d %H:%i:%S') createTime from mg_token_sell_order m "+
		"INNER JOIN `user` u ON u.uid = m.lend_uid "+
		"INNER JOIN super_node_miner s ON u.uid = s.uid  where type = ? and m.status = ? "+
		"ORDER BY scale DESC, id "+
		"DESC LIMIT ?, ?", average, TokenRentOrderRent, TokenRentOrderStatusRenting, start, per)
	if err != nil {
		Log("err", "失败 GetRentOrderList：", err.Error())
	}
	return
}

//查询发布中的租赁订单
//Auth:Xu
func GetMyRentOrderList(uid int64, per, page int) (orderList []RentListInfo, err error) {
	start := (page - 1) * per
	err = sqlx.Select(config.C.DB.DB, &orderList, "select order_no as orderNo,token_num as totalNum, deal_num, days, scale,  from_unixtime(m.create_time, '%Y-%m-%d %H:%i:%S') createTime from mg_token_sell_order m "+
		" where type = ? and status = 1 and lend_uid = ? "+
		"ORDER BY status, create_time DESC, id "+
		"DESC LIMIT ?, ?", TokenRentOrderRent, uid, start, per)
	if err != nil {
		Log("err", "失败 GetRentOrderList：", err.Error())
	}
	return
}

//查询租借订单详情
//Auth:Xu
func GetMyRentOrderInfo(orderNo string) (orderInfo RentListInfo, err error) {
	err = sqlx.Get(config.C.DB.DB, &orderInfo, "select order_no as orderNo, info,token_num as totalNum, deal_num, days, scale,  from_unixtime(m.create_time, '%Y-%m-%d %H:%i:%S') createTime from mg_token_sell_order m "+
		" where order_no = ? ", orderNo)
	if err != nil {
		Log("err", "失败 GetMyRentOrderInfo：", err.Error())
	}
	return
}

//租赁令牌-添加记录
func AddMgTokenSellOrder(db sqlx.Ext, data TokenSellOrder) error {
	_, err := db.Exec("INSERT INTO mg_token_sell_order (order_no, token_num, lend_uid, scale, days, `status`, type, info, create_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
		data.OrderNo, data.TokenNum, data.LendUid, data.Scale, data.Days, data.Status, data.Type, data.Info, data.CreateTime)
	return err
}

//租赁令牌-获取订单信息
func GetMgTokenSellOrder(db sqlx.Queryer, orderNo string) (qi TokenSellOrder, err error) {
	err = sqlx.Get(db, &qi, "SELECT * from mg_token_sell_order where order_no = ?", orderNo)
	return qi, err
}

//租赁令牌-修改令牌状态
func UpdateMgTokenSellOrderStatus(db sqlx.Ext, orderNo string, status int64) error {
	_, err := db.Exec("UPDATE mg_token_sell_order SET `status` = ? where order_no = ?", status, orderNo)
	return err
}

//租赁令牌-获取自有闲置令牌数量
func GetIdleTokenNum(db sqlx.Queryer, uid int64) (qi int64, err error) {
	err = sqlx.Get(db, &qi, "SELECT count(*) from mg_token where belong_uid = ? AND `status` = 1 AND rent_status = 1", uid)
	return qi, err
}

//租赁令牌-出租事务
func RentTrans(db *db.DBLogger, data TokenSellOrder) (bool, error) {
	var (
		tokenNum float64
		dealNum  float64
		tokenIds []int64
		tokenId  int64
	)
	//开启事务
	tx, err := db.Beginx()
	if err != nil {
		Log("info", err)
		return false, err
	}
	//修改订单信息
	result, err := tx.Exec("UPDATE mg_token_sell_order set deal_num = deal_num + ? where order_no = ?", data.DealNum, data.ParentOrderNo)
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	aff, err := result.RowsAffected()
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	if aff != 1 {
		Log("info", "影响行数为:", aff)
		_ = tx.Rollback()
		return false, err
	}
	//获取订单信息
	err = tx.QueryRowx("SELECT token_num, deal_num from mg_token_sell_order where order_no = ?", data.ParentOrderNo).Scan(&tokenNum, &dealNum)
	if err != nil {
		_ = tx.Rollback()
		return false, err
	}
	if dealNum > tokenNum {
		Log("info", "超出求租数量:")
		_ = tx.Rollback()
		return false, err
	} else if dealNum == tokenNum {
		result, err := tx.Exec("UPDATE mg_token_sell_order set `status` = 2 where order_no = ?", data.ParentOrderNo)
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		aff, err := result.RowsAffected()
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		if aff != 1 {
			Log("info", "影响行数为:", aff)
			_ = tx.Rollback()
			return false, err
		}
	}
	//插入出租记录
	result, err = tx.Exec("INSERT INTO mg_token_sell_order (order_no, parent_order_no, token_num, deal_num, rent_uid, lend_uid, scale, days, end_time, `status`, type, create_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
		data.OrderNo, data.ParentOrderNo, data.TokenNum, data.DealNum, data.RentUid, data.LendUid, data.Scale, data.Days, data.EndTime, data.Status, data.Type, data.CreateTime)
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	aff, err = result.RowsAffected()
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	if aff != 1 {
		Log("info", "影响行数为:", aff)
		_ = tx.Rollback()
		return false, err
	}
	//获取即将修改令牌id
	rows, err := tx.Query("SELECT id from mg_token where belong_uid = ? AND `status` = 1 AND rent_status = 1 limit ?", data.RentUid, data.DealNum)
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	defer rows.Close()
	for rows.Next() {
		if err := rows.Scan(&tokenId); err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		tokenIds = append(tokenIds, tokenId)
	}
	sqlStr, args, err := sqlx.In("UPDATE mg_token set rent_status = 2, rent_days = ?, rent_scale = ?, rent_time = ?, use_uid = ? where id in (?)",
		data.Days, data.Scale, data.CreateTime, data.LendUid, tokenIds)
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	//修改令牌信息
	result, err = tx.Exec(sqlStr, args...)
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	aff, err = result.RowsAffected()
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	if aff != data.DealNum {
		Log("info", "影响行数为:", aff)
		_ = tx.Rollback()
		return false, err
	}
	//插入令牌日志
	for _, tokenId := range tokenIds {
		result, err = tx.Exec("INSERT INTO `mg_token_log` (token_id, source_uid, target_uid, order_no, type, create_time) VALUES (?, ?, ?, ?, ?, ?)",
			tokenId, data.RentUid, data.LendUid, data.OrderNo, 3, data.CreateTime)
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		aff, err = result.RowsAffected()
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		if aff != 1 {
			Log("info", "影响行数为:", aff)
			_ = tx.Rollback()
			return false, err
		}
	}
	_ = tx.Commit()
	Log("info", "数据库写入成功")
	return true, nil
}

func GetNotRentNum(db sqlx.Queryer, uid int64) (qi int64, err error) {
	err = sqlx.Get(db, &qi, "SELECT sum(token_num - deal_num) FROM `mg_token_sell_order` where lend_uid = ? and `status` = 1 and type = 1", uid)
	return qi, err
}

type TokenDrawQueueReturn struct {
	Id         int64  `db:"id" json:"id"`
	TokenNum   int    `db:"token_num" json:"num"`
	OrderNo    string `db:"order_no" json:"orderNo"`
	Ranking    string `db:"ranking" json:"ranking"`
	Uid        int64  `db:"uid" json:"uid"`
	Cvn        int64  `db:"cvn" json:"totalPrice"`
	CreateTime string `db:"create_time" json:"createTime"`
}

//查询用户令牌回购排队记录
//Auth:Xu
func GetMyBuybackQueue(db sqlx.Queryer, uid int64) (list []TokenDrawQueueReturn, err error) {
	err = sqlx.Select(db, &list, "select * from (SELECT id,uid,cvn,from_unixtime(create_time, '%Y-%m-%d %H:%i') create_time,token_num,order_no,@total := @total + total1 AS ranking "+
		"FROM ("+
		"SELECT id,uid,cvn,token_num,order_no,token_num as total1,create_time FROM mg_token_draw_queue where status = 3) AS temp,"+
		"(SELECT @total := 0) AS T1) T3 "+
		"where uid = ? ORDER BY id", uid)
	if err != nil {
		Log("err", "失败 GetMyBuybackQueue：", err.Error())
	}

	return
}

func GetRentOrderExpire(db sqlx.Queryer) (qi []TokenSellOrder, err error) {
	nowTime := time.Now().Unix()
	err = sqlx.Select(db, &qi, "SELECT * FROM `mg_token_sell_order` where type = 2 and status = 2 and end_time <= ?", nowTime)
	return qi, err
}

func GetMgTokenLogByOrderNo(db sqlx.Queryer, orderNo string, t int) (qi []MgTokenLog, err error) {
	err = sqlx.Select(db, &qi, "SELECT * FROM `mg_token_log` where order_no = ? and type = ?", orderNo, t)
	return qi, err
}

func ReturnTokenTrans(db *db.DBLogger, order TokenSellOrder) (bool, error) {
	var (
		nowTime = time.Now().Unix()
	)
	mgTokenLogs, err := GetMgTokenLogByOrderNo(db, order.OrderNo, 3)
	if err != nil {
		Log("info", err)
		return false, err
	}
	mgTokenLogsLen := len(mgTokenLogs)
	if mgTokenLogsLen == 0 {
		Log("info", "未找到对应的令牌")
		return false, err
	}
	//开启事务
	tx, err := db.Beginx()
	if err != nil {
		Log("info", err)
		return false, err
	}
	result, err := tx.Exec("UPDATE mg_token_sell_order set status = 4 where order_no = ?", order.OrderNo)
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	aff, err := result.RowsAffected()
	if err != nil {
		Log("info", err)
		_ = tx.Rollback()
		return false, err
	}
	if aff != 1 {
		Log("info", "影响行数为:", aff)
		_ = tx.Rollback()
		return false, err
	}
	for _, mgTokenLog := range mgTokenLogs {
		result, err = tx.Exec("UPDATE mg_token set `status` = 1, rent_status = 1, rent_days = 0, rent_scale = 0, rent_time = 0, use_uid = belong_uid, type = 0, node_num = 0 where id = ?", mgTokenLog.TokenId)
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		aff, err = result.RowsAffected()
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		if aff != 1 {
			Log("info", "影响行数为:", aff)
			_ = tx.Rollback()
			return false, err
		}
		result, err = tx.Exec("UPDATE mg_node set token_status = 1, token_id = 0, pledge_time = 0 where token_id = ?", mgTokenLog.TokenId)
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		aff, err = result.RowsAffected()
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		if aff > TokenRouterNode {
			Log("info", "影响行数为:", aff)
			_ = tx.Rollback()
			return false, err
		}
		result, err = tx.Exec("INSERT INTO `mg_token_log` (token_id, source_uid, target_uid, order_no, type, create_time) VALUES (?, ?, ?, ?, ?, ?)",
			mgTokenLog.TokenId, order.LendUid, order.RentUid, order.OrderNo, 4, nowTime)
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		aff, err = result.RowsAffected()
		if err != nil {
			Log("info", err)
			_ = tx.Rollback()
			return false, err
		}
		if aff != 1 {
			Log("info", "影响行数为:", aff)
			_ = tx.Rollback()
			return false, err
		}
	}
	_ = tx.Commit()
	Log("info", "数据库写入成功")
	return true, nil

}

type TokenDrawQueueBefore struct {
	TokenNum   int    `db:"token_num" json:"token_num"`
	Uid        int64  `db:"uid" json:"uid"`
	Cvn        int64  `db:"cvn" json:"cvn"`
	OrderNo    string `db:"order_no" json:"orderNo"`
	Ranking    int    `db:"ranking" json:"ranking"`
	CreateTime int64  `db:"create_time" json:"createTime"`
}

//查询令牌回购前几位
//Auth:Xu
func GetBuybackQueueBefore(db sqlx.Queryer, num int) (list []TokenDrawQueueBefore, err error) {
	err = sqlx.Select(db, &list, "SELECT create_time, token_num,uid,cvn,order_no, (@sum := @sum + token_num) AS ranking FROM mg_token_draw_queue,(select @sum := 0) AS T1 where status = ? and @sum < ? ORDER BY id ", TokenQueueStatusRanking, num)
	if err != nil {
		Log("err", "查询令牌回购队列待处理的任务失败", err.Error())
	}
	return
}

//查询令牌回购用户最小排名
//Auth:Xu
func GetUserBuybackQueueRank(db sqlx.Queryer, uid int64) (num int, err error) {
	err = sqlx.Get(db, &num, "select sumnum from (SELECT create_time, token_num,uid, (@sum := @sum + token_num) AS sumnum FROM mg_token_draw_queue,(select @sum := 0) AS T1 where status = ? ORDER BY id ) temp where uid = ? ORDER BY create_time limit 1", TokenQueueStatusRanking, uid)
	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		Log("err", "GetUserBuybackQueueRank 失败", err.Error())
	}
	return
}

//获取矿工平均收益
//Author: TXG
func GetMinerIncomeAbility(db sqlx.Queryer, uid int64) (qi float64, err error) {
	err = sqlx.Get(db, &qi, "SELECT income_ability FROM `super_node_miner` where uid = ?", uid)
	return qi, err
}
